<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" >

<title>FineUIGrid导出图片的实践 | 没想好的个人博客</title>

<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">

<!-- <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.7.2/css/all.css" integrity="sha384-fnmOCqbTlWIlj8LyTjo7mOUStjsKC4pOpQbqyi7RrhN7udi9RwhKkMHpvLbHG9Sr" crossorigin="anonymous"> -->
   <!-- Font Awesome CSS-->
   <link href="https://cdn.bootcss.com/font-awesome/5.13.0/css/all.css" rel="stylesheet">

   <link rel="stylesheet" href="https://cdn.bootcss.com/font-awesome/5.7.2/css/all.min.css" integrity="sha384-fnmOCqbTlWIlj8LyTjo7mOUStjsKC4pOpQbqyi7RrhN7udi9RwhKkMHpvLbHG9Sr" crossorigin="anonymous">

<link rel="shortcut icon" href="https://shixixiyue.gitee.io//favicon.ico?v=1709791195950">
<link rel="stylesheet" href="https://shixixiyue.gitee.io//styles/main.css">



<link rel="stylesheet" href="https://unpkg.com/aos@next/dist/aos.css" />
<script src="https://jsd.onmicrosoft.cn/npm/vue/dist/vue.js"></script>


<style>
  .hljs-ln{
    padding-bottom: 1.2rem;
    margin: 0rem !important;
  }
  .hljs-ln td {
    border: none !important;
    padding: 2px 6px !important;
    line-height: 1.2rem;
    font-size: 1rem;
  }
  .hljs-ln tr td:first-child  {
    color: cadetblue;
    border-right: 1px solid !important;
  }
  .hljs-ln tr {
    border: none !important;
  }
</style>

    <meta name="description" content="因为特定场景，想到使用 html2canvas 给Grid导出图片，感觉比导出Excel好使一些，这里忽略了Grid的横向滚动条，遇见的可以作为参考，导出的效果还是不错的


1. 需要解决的问题

1.1 html2canvas 本身的跨..." />
    <meta name="keywords" content="FineUICore" />
  </head>
  <body>
    <div id="app" class="main">

      <div class="sidebar" :class="{ 'full-height': menuVisible }">
  <div class="top-container" data-aos="fade-right">
    <div class="top-header-container">
      <a class="site-title-container" href="https://shixixiyue.gitee.io/">
        <img src="https://shixixiyue.gitee.io//images/avatar.png?v=1709791195950" class="site-logo">
        <h1 class="site-title">没想好的个人博客</h1>
      </a>
      <div class="menu-btn" @click="menuVisible = !menuVisible">
        <div class="line"></div>
      </div>
    </div>
    <div>
      
        
          <a href="https://shixixiyue.gitee.io/" class="site-nav">
            首页
          </a>
        
      
        
          <a href="https://shixixiyue.gitee.io//tag/4gOKry8Qv/" class="site-nav">
            .Net Core
          </a>
        
      
        
          <a href="https://shixixiyue.gitee.io//tag/njETBpqXu/" class="site-nav">
            FineUICore
          </a>
        
      
        
          <a href="https://shixixiyue.gitee.io//tag/XyXyeLOBB" class="site-nav">
            读书笔记
          </a>
        
      
        
          <a href="https://shixixiyue.gitee.io//tag/TyVX6hfCJ" class="site-nav">
            Vue笔记
          </a>
        
      
        
          <a href="https://shixixiyue.gitee.io//tag/3Hz2sPtzO/" class="site-nav">
            产品
          </a>
        
      
        
          <a href="https://shixixiyue.gitee.io//tag/Db1wShB_E" class="site-nav">
            其他
          </a>
        
      
        
          <a href="https://shixixiyue.gitee.io/" class="site-nav">
            一一一一一一一一一
          </a>
        
      
        
          <a href="https://shixixiyue.gitee.io//archives" class="site-nav">
            文章归档
          </a>
        
      
        
          <a href="https://shixixiyue.gitee.io//tags" class="site-nav">
            全部标签
          </a>
        
      
    </div>
  </div>
  <div class="bottom-container" data-aos="flip-up" data-aos-offset="0">
    <div class="social-container">
      
        
      
        
      
        
      
        
      
        
      
    </div>
    <div class="site-description">
      没事啥都说
    </div>
    <div class="site-footer">
      QQ：<a href="tencent://message/?uin=935732994&Site=''&Menu=yes">935732994<a/> <a href="http://beian.miit.gov.cn">京ICP备2024045283号<a/> | <a class="rss" href="https://shixixiyue.gitee.io//atom.xml" target="_blank">RSS</a>
    </div>
  </div>
</div>


      <div class="main-container">
        <div class="content-container" data-aos="fade-up">
          <div class="post-detail">
            <h2 class="post-title">FineUIGrid导出图片的实践</h2>
            <div class="post-date">2023-09-18
            
                
                  <a href="https://shixixiyue.gitee.io/tag/njETBpqXu/" class="tag">
                    FineUICore
                  </a>
                
            </div>
            
            <div class="post-content" v-pre>
              <p>因为特定场景，想到使用 <code>html2canvas</code> 给Grid导出图片，感觉比导出Excel好使一些，这里忽略了Grid的横向滚动条，遇见的可以作为参考，导出的效果还是不错的</p>
<!-- more -->
<p><ul class="markdownIt-TOC">
<li><a href="#1-%E9%9C%80%E8%A6%81%E8%A7%A3%E5%86%B3%E7%9A%84%E9%97%AE%E9%A2%98">1. 需要解决的问题</a>
<ul>
<li><a href="#11-html2canvas-%E6%9C%AC%E8%BA%AB%E7%9A%84%E8%B7%A8%E5%9F%9F">1.1 html2canvas 本身的跨域</a></li>
<li><a href="#12-%E6%BA%A2%E5%87%BA%E5%92%8C%E6%BB%9A%E5%8A%A8%E6%9D%A1">1.2 溢出和滚动条</a></li>
<li><a href="#13-%E6%A0%B7%E5%BC%8F%E7%9A%84%E4%BF%AE%E6%94%B9">1.3 样式的修改</a></li>
<li><a href="#14-%E9%81%BF%E5%85%8D%E5%89%8D%E5%8F%B0%E9%81%AE%E6%8C%A1">1.4 避免前台遮挡</a></li>
<li><a href="#15-%E5%A2%9E%E5%8A%A0%E6%B0%B4%E5%8D%B0">1.5 增加水印</a></li>
</ul>
</li>
<li><a href="#2-%E6%BA%90%E7%A0%81">2 源码</a>
<ul>
<li><a href="#21-outputimg-%E7%9C%8B%E6%B3%A8%E9%87%8A%E5%B0%B1%E8%A1%8C%E4%BA%86">2.1 outputimg 看注释就行了</a></li>
<li><a href="#22-watermarkjs-%E6%B0%B4%E5%8D%B0">2.2 watermark.js 水印</a></li>
</ul>
</li>
</ul>
</p>
<h1 id="1-需要解决的问题">1. 需要解决的问题</h1>
<h2 id="11-html2canvas-本身的跨域">1.1 html2canvas 本身的跨域</h2>
<p>如果直接使用 <code>html2canvas</code> 导出 F.ui.Grid.el 会出现跨域问题，滚动条问题，样式错位问题 等；所以还是采用 Wrapper 或者copy 、shadow 的方式，即复制 F.ui.Grid.el 放到新的div中，再导出；</p>
<h2 id="12-溢出和滚动条">1.2 溢出和滚动条</h2>
<p>还有一个问题是滚动条，<code>html2canvas</code> 也会截取滚动条，如果溢出，那导出就看不见了，这里因为数据本身不会出现横向滚动条，而且没有分页，所以隐藏横向滚动条，纵向高度百分百以显示全部；</p>
<h2 id="13-样式的修改">1.3 样式的修改</h2>
<p>注意在复制后对 Wrapper 中的Grid进行修饰调整，比如有的列需要改为换行（不然挡上也显示不出来），取消选中的行，删除没用的按钮，等；</p>
<h2 id="14-避免前台遮挡">1.4 避免前台遮挡</h2>
<p>新增的容器 Wrapper 元素需要实实在在新增到<code>document.body</code> 中，不能遮挡原始的 Grid 否则会很奇怪，这里使用 <code>translateY</code> 进行偏移，直接偏移一个Grid的高度，就挡不上了，因为没有Y的滚动条，所以看不出来对于前台是无感的；</p>
<h2 id="15-增加水印">1.5 增加水印</h2>
<p>后来有个需求是增加水印，因为<code>html2canvas</code> 不支持 <code>opacity</code> 所以不能使用 opacity 控制水印，应该使用 <code>canvas</code> 这里找到了一个插件一并放出；</p>
<h1 id="2-源码">2 源码</h1>
<h2 id="21-outputimg-看注释就行了">2.1 outputimg 看注释就行了</h2>
<pre><code>/**导出图片 */
function outputimg() {
  const divElement = document.getElementById('Grid1');

  // 创建一个新的div元素，避免滚动条
  const screenshotWrapper = document.createElement('div');
  screenshotWrapper.style.overflow = 'hidden';
  screenshotWrapper.style.height = '100%';
  screenshotWrapper.style.width = '100%';
  screenshotWrapper.style.transform = `translateY(${divElement.offsetHeight}px)`;//偏移 以不显示 

  // 创建一个新的div元素，用于容纳截图内容
  const screenshotContent = document.createElement('div');
  screenshotContent.style.width = '100%';

  // 将原始Grid的内容复制到新的div元素中
  screenshotContent.innerHTML = divElement.innerHTML;
  // 将新的div元素添加到截图容器中
  screenshotWrapper.appendChild(screenshotContent);

  //复制之后就可以对Wrapper中的Grid进行操作
  //解决滚动条的问题 Grid原始的高度直接不要
  $(screenshotContent).find(`.f-panel-body`).height(`unset`);
  $(screenshotContent).find(`.f-grid-bodyct`).height(`unset`);
  //选中的和激活的行样式都删除
  $(screenshotContent).find(`.f-grid-row-selected`).removeClass(`f-grid-row-selected`);
  $(screenshotContent).find(`.f-state-active`).removeClass(`f-state-active`);

  //删除按钮或其他
  $(screenshotContent).find(`.btn`).remove();
  //这里做了某一列的换行
  $(screenshotContent).find(`[data-columnid=&quot;MB003&quot;] .f-grid-cell-inner`).css({
      &quot;whiteSpace&quot;: &quot;normal&quot;,
      &quot;wordBreak&quot;: &quot;break-all&quot;
  });
  
  //添加到body中
  document.body.appendChild(screenshotWrapper);
  //增加水印
  new watermark({
      text: `${thisusername} ${thisdate}`,  //水印内容
      render: screenshotContent,
  });
  // 使用html2canvas进行截图操作
  html2canvas(screenshotWrapper, { useCORS: true }).then(canvas =&gt; {
      // 创建一个新的图片元素
      //const image = new Image();
      //image.src = canvas.toDataURL('image/png');
      // 将图片元素添加到页面中
      //document.body.appendChild(image);
      const dataURL = canvas.toDataURL('image/png');
      // 创建一个虚拟的下载链接
      const downloadLink = document.createElement('a');
      downloadLink.href = dataURL;
      downloadLink.download = '导出图片.png';
      // 触发点击事件进行下载
      downloadLink.click();
      $(screenshotWrapper).remove();
  });
}
</code></pre>
<h2 id="22-watermarkjs-水印">2.2 watermark.js 水印</h2>
<pre><code>(function (window) {
    /**
     * js水印插件
     * 自动在body底部生成一个水印
     * auth：zhangdaren
     * date：20190218
     * version: v1.0
     *
     * */
    var canvas = null;
    var setting = {
        render: document.body,
        text: &quot;水印测试&quot;, //水印内容，将来支持数组和图片
        fontSize: &quot;24&quot;, //字体大小
        color: &quot;#000&quot;, //水印字体颜色
        opacity: 0.1, //水印透明度
        rotate: 20, //水印旋转角度
        spaceX: 10, //间隔
        spaceY: 10, //间隔
    };
    var ww; //屏幕宽度
    var wh; //屏幕高度
    var ctx;

    function watermark(option) {
        return new watermark.prototype.init(option);
    }
    watermark.prototype = {
        constructor: watermark,
        init: function (option) {
            if (option &amp;&amp; Object.keys(option).length &gt; 0) {
                //
                setting.text = option.text;
                setting.fontSize = option.fontSize || setting.fontSize;
                setting.color = option.color || setting.color;
                setting.opacity = option.opacity || setting.opacity;
                setting.rotate = option.rotate || setting.rotate;
                setting.spaceX = option.spaceX || setting.spaceX;
                setting.spaceY = option.spaceY || setting.spaceY;
                setting.render = option.render || setting.render;
            }

            ww = setting.render.clientWidth;
            wh = setting.render.clientHeight;
            //
            canvas = document.createElement(&quot;canvas&quot;);
            canvas.style = &quot;position:absolute; z-index:0; width:100%;height:100%;left:0;top:0&quot;;
            canvas.setAttribute(&quot;width&quot;, ww);
            canvas.setAttribute(&quot;height&quot;, wh);
            setting.render.append(canvas);

            ctx = canvas.getContext(&quot;2d&quot;);

            //渲染
            watermark.prototype.render();
        },
        render() {
            watermark.prototype.clear();
            //
            ctx.font = setting.fontSize + &quot;px Calibri&quot;;
            ctx.textAlign = 'center';
            ctx.textBaseline = 'bottom';
            ctx.fillStyle = setting.color;
            ctx.globalAlpha = setting.opacity; //这里是设置全局的透明度

            //获取文本宽高
            var txtWidth = ctx.measureText(setting.text).width;
            //单个文字宽高
            var singleTxtHeight = ctx.measureText(&quot;鹏&quot;).width;
            //横向数量，这里加2是为了补全一些情况
            var columnNum = parseInt(ww / txtWidth + 2);
            //文字的高度，与旋转的角度有关
            var txtHeight = Math.abs(singleTxtHeight * Math.sin(setting.rotate * Math.PI / 180) * setting.text.length); // * setting.text.length;
            //行数
            var rowNum = parseInt(wh / txtHeight);

            for (var i = 0; i &lt; rowNum; i++) {
                var y = (txtHeight + setting.spaceY) * i;
                for (var j = 0; j &lt; columnNum; j++) {
                    var x = (txtWidth + setting.spaceX) * j;
                    //保存上次状态
                    ctx.save();
                    //旋转canvas
                    rotateContext(ctx, x, y, -setting.rotate);
                    ctx.fillText(setting.text, x, y);
                    //回到上次状态 //用于重置canvas的旋转
                    ctx.restore();
                }
            }
        },
        clear() {
            ctx.clearRect(0, 0, ww, wh);
        }
    };

    //确保是以(x,y)为中心进行旋转，而不是简单的以画布原点旋转
    function rotateContext(ctx, x, y, degree) {
        ctx.translate(x, y);
        ctx.rotate(degree * Math.PI / 180);
        ctx.translate(-x, -y);
    }

    function onResize() {
        //重新获取window宽高
        ww = setting.render.clientWidth;
        wh = setting.render.clientHeight;
        //重新设置canvas宽高
        canvas.setAttribute(&quot;width&quot;, ww);
        canvas.setAttribute(&quot;height&quot;, wh);
        //重绘
        watermark.prototype.clear();
        watermark.prototype.render();
    }

    window.onresize = function () {
        throttle(onResize, window);
    }

    //函数节流
    function throttle(method, context) {
        clearTimeout(method.tId);
        method.tId = setTimeout(function () {
            method.call(context);
        }, 500);
    }

    window.watermark = watermark;
})(window);
</code></pre>
<figure data-type="image" tabindex="1"><img src="https://shixixiyue.gitee.io//post-images/1695023023086.png" alt="" loading="lazy"></figure>
<figure data-type="image" tabindex="2"><img src="https://shixixiyue.gitee.io//post-images/1695023153323.png" alt="" loading="lazy"></figure>

            </div>
            
              <div class="tag-container">
                
                  <a href="https://shixixiyue.gitee.io/tag/njETBpqXu/" class="tag">
                    FineUICore
                  </a>
                
              </div>
            
            
              <div class="next-post">
                <div class="next">下一篇</div>
                <a href="https://shixixiyue.gitee.io/mysql-zhu-cong-tong-bu-jiao-ben/">
                  <h3 class="post-title">
                    Mysql主从同步脚本
                  </h3>
                </a>
              </div>
            

            

          </div>

        </div>
      </div>
    </div>

    <script src="https://unpkg.com/aos@next/dist/aos.js"></script>
<script type="application/javascript">

AOS.init();

var app = new Vue({
  el: '#app',
  data: {
    menuVisible: false,
  },
})

</script>


  <script src="//jsd.onmicrosoft.cn/gh/highlightjs/cdn-release@11.5.1/build/highlight.min.js"></script>
  <script>
    hljs.initHighlightingOnLoad();
    //hljs.registerLanguage('mermaid', window.hljsDefineMermaid);
  </script>
  <script src="//jsd.onmicrosoft.cn/npm/highlightjs-line-numbers.js@2.8.0/dist/highlightjs-line-numbers.min.js"></script>
  <script>
    hljs.initLineNumbersOnLoad({
      singleLine: true
    });
  </script>
 <script src="https://jsd.onmicrosoft.cn/npm/mermaid/dist/mermaid.min.js"></script>
   <script>
    mermaid.initialize({
      startOnLoad: true,
      securityLevel: 'loose',
    });
  </script>





  </body>
</html>
